using System;
using System.Text;
using System.Security.Cryptography;

/**********************************************************
 * Andrea Celin 2007
 * The enc/dec code is based on Codeproject article.
 * 
 * You can use and change this code, but NOT remove
 * this text and/or my name. You may add your personal message
 * under this box.
 **********************************************************/
namespace WLMAddin
{
    /// <summary>
    /// Encryption Decryption for messages
    /// </summary>
    public class EDcrypto
    {
        /// <summary>
        /// Check if the text could be decrypted with given password
        /// </summary>
        /// <param name="text">under control text</param>
        /// <param name="password">password</param>
        /// <returns>true, the text is encrypted with given password</returns>
        public static bool IsEncryptedText(string text, string password)
        {
            bool m_isencrypt = true;
            try { EDcrypto.Decrypt(text, password); }
            catch { m_isencrypt = false; }

            return m_isencrypt;
        }

        /// <summary>
        /// Encrypt a byte array into a byte array using a key and an IV 
        /// </summary>
        /// <param name="clearData">Incoming data</param>
        /// <param name="Key">Key used to encrypt</param>
        /// <param name="IV">Initialization vector</param>
        /// <returns>byte array encrypted</returns>
        private static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream to accept the encrypted bytes 
            System.IO.MemoryStream m_memstream = new System.IO.MemoryStream();

            // Create a symmetric algorithm. 
            Rijndael m_algor = Rijndael.Create();

            // Now set the key and the IV. 
            // We need the IV (Initialization Vector) 
            m_algor.Key = Key;
            m_algor.IV = IV;

            // Create a CryptoStream through which we are going to be pumping our data. 
            CryptoStream m_crystream = new CryptoStream(m_memstream, m_algor.CreateEncryptor(), CryptoStreamMode.Write);

            // Write the data and make it do the encryption 
            m_crystream.Write(clearData, 0, clearData.Length);

            // Close the crypto stream (or do FlushFinalBlock). 
            m_crystream.Close();

            // Now get the encrypted data from the MemoryStream.
            // Some people make a mistake of using GetBuffer() here,
            // which is not the right way. 
            byte[] encryptedData = m_memstream.ToArray();

            return encryptedData;
        }

        /// <summary>
        /// Decrypt a byte array into a byte array using a key and an IV 
        /// </summary>
        /// <param name="cipherData">Encrypted data</param>
        /// <param name="Key">Key used to decrypt</param>
        /// <param name="IV">Initialization vector</param>
        /// <returns>byte array decrypted</returns>
        private static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream that is going to accept the decrypted bytes 
            System.IO.MemoryStream m_memstream = new System.IO.MemoryStream();

            // Create a symmetric algorithm. 
            Rijndael m_algor = Rijndael.Create();

            // Now set the key and the IV. 
            // We need the IV (Initialization Vector)
            m_algor.Key = Key;
            m_algor.IV = IV;

            // Create a CryptoStream through which we are going to be pumping our data. 
            CryptoStream m_crystream = new CryptoStream(m_memstream, m_algor.CreateDecryptor(), CryptoStreamMode.Write);

            // Write the data and make it do the decryption 
            m_crystream.Write(cipherData, 0, cipherData.Length);

            // Close the crypto stream (or do FlushFinalBlock). 
            m_crystream.Close();

            // Now get the decrypted data from the MemoryStream. 
            // Some people make a mistake of using GetBuffer() here,
            // which is not the right way. 
            byte[] decryptedData = m_memstream.ToArray();

            return decryptedData;
        }

        /// <summary>
        /// Decrypt a string into a string using a password
        /// </summary>
        /// <param name="clearText">Incoming encrypted text</param>
        /// <param name="Password">Password used to decrypt</param>
        /// <returns>decrypted string</returns>
        public static string Decrypt(string cipherText, string Password)
        {
            // First we need to turn the input string into a byte array. 
            // We presume that Base64 encoding was used 
            byte[] cipherBytes = Convert.FromBase64String(cipherText);

            // Then, we need to turn the password into Key and IV 
            // We are using salt to make it harder to guess our key
            // using a dictionary attack - 
            // trying to guess a password by enumerating all possible words. 
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 
            0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the decryption using
            // the function that accepts byte arrays. 
            // Using PasswordDeriveBytes object we are first
            // getting 32 bytes for the Key 
            // (the default Rijndael key length is 256bit = 32bytes)
            // and then 16 bytes for the IV. 
            // IV should always be the block size, which is by
            // default 16 bytes (128 bit) for Rijndael. 
            // If you are using DES/TripleDES/RC2 the block size is
            // 8 bytes and so should be the IV size. 
            // You can also read KeySize/BlockSize properties off
            // the algorithm to find out the sizes. 
            byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 
            // A common mistake would be to use an Encoding class for that.
            // It does not work 
            // because not all byte values can be represented by characters. 
            // We are going to be using Base64 encoding that is 
            // designed exactly for what we are trying to do. 
            return System.Text.Encoding.Unicode.GetString(decryptedData);
        }

        /// <summary>
        /// Encrypt a string into a string using a password
        /// </summary>
        /// <param name="clearText">Incoming text</param>
        /// <param name="Password">Password used to encrypt</param>
        /// <returns>encrypted string</returns>
        public static string Encrypt(string clearText, string Password)
        {
            // First we need to turn the input string into a byte array. 
            byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);

            // Then, we need to turn the password into Key and IV 
            // We are using salt to make it harder to guess our key
            // using a dictionary attack - 
            // trying to guess a password by enumerating all possible words. 
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the encryption using the
            // function that accepts byte arrays. 
            // Using PasswordDeriveBytes object we are first getting
            // 32 bytes for the Key 
            // (the default Rijndael key length is 256bit = 32bytes)
            // and then 16 bytes for the IV. 
            // IV should always be the block size, which is by default
            // 16 bytes (128 bit) for Rijndael. 
            // If you are using DES/TripleDES/RC2 the block size is
            // 8 bytes and so should be the IV size. 
            // You can also read KeySize/BlockSize properties off
            // the algorithm to find out the sizes. 
            byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 
            // A common mistake would be to use an Encoding class for that.
            //It does not work because not all byte values can be
            // represented by characters. 
            // We are going to be using Base64 encoding that is designed
            //exactly for what we are trying to do. 
            return Convert.ToBase64String(encryptedData);
        }
    }
}
